package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.heima.client.article.IArticleClient;
import com.heima.common.exception.CustomException;
import com.heima.common.redis.CacheService;
import com.heima.file.service.impl.MinIOFileTemplate;
import com.heima.model.common.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.pojos.wemedia.WmChannel;
import com.heima.model.common.pojos.wemedia.WmNews;
import com.heima.model.common.pojos.wemedia.WmSensitive;
import com.heima.model.common.pojos.wemedia.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.config.Tess4jClient;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.BaiduScanService;
import com.heima.wemedia.service.WmAutoScanService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class WmAutoScanServiceImpl implements WmAutoScanService {
    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Autowired
    private BaiduScanService baiduScanService;
    @Autowired
    private IArticleClient articleClient;
    @Autowired
    private WmUserMapper wmUserMapper;
    @Autowired
    private WmChannelMapper wmChannelMapper;
    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private Tess4jClient tess4jClient;
    @Autowired
    private MinIOFileTemplate minIOTemplate;

    /**
     * 自动审核文章信息
     *
     * @param newId
     */
    @Override
    public void autoScan(Integer newId) {
        //1. 校验文章信息及状态：1（已提交，待审核）
        WmNews wmNews = wmNewsMapper.selectById(newId);
        if (wmNews == null || wmNews.getStatus() != WmNews.Status.SUBMIT.getCode())
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);

        //2. 提取出文章中的所有的文本与图片数据
        Map<String, Object> textAndImg = getTextAndImgInNews(wmNews);

        //3. 先审核文字
        boolean textIsPass = scanText((String) textAndImg.get("text"),wmNews);
        if (!textIsPass)return;

        //4. 再审核图片
        boolean imgIsPass = scanImg((Set<String>) textAndImg.get("img"),wmNews);
        if (!imgIsPass)return;

        //5. 发起远程feign调用，调用article微服务，将文章信息同步到app端
        saveOrUpdateArticle(wmNews);
    }

    //审核文章中的文本内容
    private boolean scanText(String text, WmNews wmNews) {
        //1. 先内审：走自定义敏感词库过滤
        //1.1 加载系统敏感词数据
        //缓存优化：先查缓存，缓存有：直接用，缓存没有：再查数据库，并将查到的数据添加到缓存
        String sensitivesJSON = cacheService.get("sensitives");
        List<String> sensitiveList = JSONArray.parseArray(sensitivesJSON, String.class);
        //缓存没有：再查数据库
        if (sensitiveList == null) {
            List<WmSensitive> wmSensitives = wmSensitiveMapper.selectList(null);
            sensitiveList = wmSensitives.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
            //添加到缓存
            cacheService.set("sensitives", JSON.toJSONString(sensitiveList));
        }
        //1.2 初始化DFA算法字典
        SensitiveWordUtil.initMap(sensitiveList);
        //1.3 调用DFA算法进行指定文本的敏感词过滤
        Map<String, Integer> resMap = SensitiveWordUtil.matchWords(text);
        if (resMap != null && resMap.size() >0){
            updateNewsStatus(WmNews.Status.FAIL.getCode(),"审核失败：文本内容违规!",wmNews);
            return false;
        }

        //2. 再外审：走第三方文本审核接口：百度云API
        Integer conclusionType = baiduScanService.scanText(text);
        if (conclusionType == 2){
            updateNewsStatus(WmNews.Status.FAIL.getCode(),"审核失败：文本内容违规!",wmNews);
            return false;
        }else if (conclusionType == 3 || conclusionType == 4){
            updateNewsStatus(WmNews.Status.ADMIN_AUTH.getCode(),"待人工审核：文本内容疑似违规!",wmNews);
            return false;
        }
        return true;
    }

    //审核文章中的图片内容
    private boolean scanImg(Set<String> imgUrls, WmNews wmNews) {
        //1. 先内审：基于tess4j进行文本OCR图文识别
        try {
            for (String imgUrl : imgUrls) {
                //将图片url转为BufferedImage
                byte[] bytes = minIOTemplate.downLoadFile(imgUrl);
                InputStream is = new ByteArrayInputStream(bytes);
                BufferedImage image = ImageIO.read(is);
                //提取出图片中的文字
                String textInImg = tess4jClient.doOCR(image);
                //如果图片中包含文字，则开启文本审核
                if (StringUtils.isNotEmpty(textInImg)){
                    boolean flag = scanText(textInImg, wmNews);
                    if (!flag){
                        updateNewsStatus(WmNews.Status.FAIL.getCode(),"审核失败：图片中包含违规文本内容!",wmNews);
                        return false;
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }


        //2. 再外审：走第三方图片审核接口：百度云API
        for (String imgUrl : imgUrls) {
            Integer conclusionType = baiduScanService.scanImg(imgUrl);
            if (conclusionType == 2){
                updateNewsStatus(WmNews.Status.FAIL.getCode(),"审核失败：文本内容违规!",wmNews);
                return false;
            }else if (conclusionType == 3 || conclusionType == 4){
                updateNewsStatus(WmNews.Status.ADMIN_AUTH.getCode(),"待人工审核：文本内容疑似违规!",wmNews);
                return false;
            }
        }

        return true;
    }

    //远程调用article微服务，将文章信息同步到app端
    private void saveOrUpdateArticle(WmNews wmNews) {
        ArticleDto articleDto = new ArticleDto();
        //准备数据
        articleDto.setContent(wmNews.getContent());
        BeanUtils.copyProperties(wmNews,articleDto);
        //单独处理以下数据：id,authorId,authorName,channelName,layout
        if (wmNews.getArticleId() != null){
            articleDto.setId(wmNews.getArticleId());
        }
        //查询作者信息
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (wmUser != null){
            articleDto.setAuthorId(Long.valueOf(wmNews.getUserId()));
            articleDto.setAuthorName(wmUser.getName());
        }
        //查询频道信息
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if (wmChannel != null){
            articleDto.setChannelId(wmChannel.getId());
            articleDto.setChannelName(wmChannel.getName());
        }
        articleDto.setLayout(wmNews.getType());

        //发送远程请求
        ResponseResult resp = articleClient.syncArticle(articleDto);
        //根据article微服务响应的结果来处理文章信息：articleId，status,reason
        if (resp.getCode() == 200){
            String articleId = (String) resp.getData();
            wmNews.setArticleId(Long.valueOf(articleId));
            updateNewsStatus(WmNews.Status.PUBLISHED.getCode(),"审核成功",wmNews);
        }
    }

    //修改文章状态
    private void updateNewsStatus(Short status,String reason,WmNews wmNews){
        wmNews.setStatus(status);
        wmNews.setReason(reason);
        //修改文章信息
        wmNewsMapper.updateById(wmNews);
    }

    //提取文章的文字和图片
    private Map<String,Object> getTextAndImgInNews(WmNews wmNews) {
        //定义map作为总的结果的存放
        Map<String,Object> res = new HashMap<>();
        //定义StringBuilder作为总的文字的存放
        StringBuilder sb = new StringBuilder(wmNews.getTitle());
        //存入标签label
        sb.append(wmNews.getLabels());
        //定义Set作为总的图片的存放
        Set<String> urls = new HashSet<>();

        JSONArray jsonArray = JSONArray.parseArray(wmNews.getContent());
        for (Object obj : jsonArray) {
            JSONObject jsonObject = JSONObject.parseObject(obj.toString());
            if ("image".equals(jsonObject.getString("type"))){
                String imgUrl = jsonObject.getString("value");
                urls.add(imgUrl);
            }
            if ("text".equals(jsonObject.getString("type"))){
                String text = jsonObject.getString("value");
                sb.append(text);
            }
        }

        //提取封面图片
        String images = wmNews.getImages();
        String[] split = images.split(",");
        Collections.addAll(urls,split);

        //将所有的文字存入最终的容器map
        res.put("text",sb.toString());
        //将所有的图片存入最终的容器map
        res.put("img",urls);
        return res;
    }
}